﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using Test.Expressions;

namespace Test
{
	
		public static class ExpressionVisitorExtensions
		{
			/// <summary>
			/// i.e. anonymous type returns false.
			/// </summary>
			/// <param name="type"></param>
			/// <returns></returns>
			public static bool CanInferType(this Type type) {
				return ! (type.Namespace == null
					&& type.ReflectedType == null
					&& type.BaseType == typeof(System.Object));
			}

			public static object GetValue(this MemberExpression member) {
				var objectMember = Expression.Convert(member, typeof(object));

				var getterLambda = Expression.Lambda<Func<object>>(objectMember);

				var getter = getterLambda.Compile();

				return getter();
			}
			public static bool IsQuery(this ExpressionVisitor visitor, Expression expression, out Type elementType) {
				elementType = TypeHelper.GetElementType(expression.Type);
				return elementType != null && typeof(IQueryable<>).MakeGenericType(elementType).IsAssignableFrom(expression.Type);
			}
			public static string GetOperator(this ExpressionVisitor visitor, string methodName) {
				switch (methodName) {
					case "Add": return "+";
					case "Subtract": return "-";
					case "Multiply": return "*";
					case "Divide": return "/";
					case "Negate": return "-";
					case "Remainder": return "%";
					default: return null;
				}
			}

			public static string GetOperator(this ExpressionVisitor visitor, UnaryExpression u) {
				switch (u.NodeType) {
					case ExpressionType.Negate:
					case ExpressionType.NegateChecked:
						return "-";
					case ExpressionType.UnaryPlus:
						return "+";
					case ExpressionType.Not:
						return IsBoolean( u.Operand.Type) ? "NOT" : "~";
					default:
						return "";
				}
			}
			public static string GetOperator(this ExpressionVisitor visitor, BinaryExpression b) {
				return GetOperator(visitor, b.NodeType);
			}
			public static string GetOperator(this ExpressionVisitor visitor, ExpressionType nodeType) {
				switch (nodeType) {
					case ExpressionType.And:
					case ExpressionType.AndAlso:
						return "AND";
					case ExpressionType.Or:
					case ExpressionType.OrElse:
						return "OR";
					case ExpressionType.Equal:
						return "=";
					case ExpressionType.NotEqual:
						return "<>";
					case ExpressionType.LessThan:
						return "<";
					case ExpressionType.LessThanOrEqual:
						return "<=";
					case ExpressionType.GreaterThan:
						return ">";
					case ExpressionType.GreaterThanOrEqual:
						return ">=";
					case ExpressionType.Add:
					case ExpressionType.AddChecked:
						return "+";
					case ExpressionType.Subtract:
					case ExpressionType.SubtractChecked:
						return "-";
					case ExpressionType.Multiply:
					case ExpressionType.MultiplyChecked:
						return "*";
					case ExpressionType.Divide:
						return "/";
					case ExpressionType.Modulo:
						return "%";
					case ExpressionType.ExclusiveOr:
						return "^";
					default:
						return "";
				}
			}

			public static string GetAggregateName(this ExpressionVisitor visitor, string methodName) {
				return GetAggregateName(visitor, GetAggregateDbExpressionType(visitor, methodName));
			}

			public static string GetAggregateName(this ExpressionVisitor visitor, DbExpressionType aggregateType) {
				switch (aggregateType) {
					case DbExpressionType.Count: return "COUNT";
					case DbExpressionType.Min: return "MIN";
					case DbExpressionType.Max: return "MAX";
					case DbExpressionType.Sum: return "SUM";
					case DbExpressionType.Average: return "AVG";
					default: throw new Exception(string.Format("Unknown aggregate type: {0}", aggregateType));
				}
			}
			public static DbExpressionType GetAggregateDbExpressionType(this ExpressionVisitor visitor, string methodName) {
				switch (methodName) {
					case "Count": return DbExpressionType.Count;
					case "Min": return DbExpressionType.Min;
					case "Max": return DbExpressionType.Max;
					case "Sum": return DbExpressionType.Sum;
					case "Average": return DbExpressionType.Average;
					default: throw new Exception(string.Format("Unknown aggregate type: {0}", methodName));
				}
			}

			public static bool IsBoolean(this Type type) {
				return type == typeof(bool) || type == typeof(bool?);
			}

			public static bool IsPredicate(this Expression expr) {
				switch (expr.NodeType) {
					case ExpressionType.And:
					case ExpressionType.AndAlso:
					case ExpressionType.Or:
					case ExpressionType.OrElse:
						return IsBoolean(((BinaryExpression)expr).Type);
					case ExpressionType.Not:
						return IsBoolean( ((UnaryExpression)expr).Type);
					case ExpressionType.Equal:
					case ExpressionType.NotEqual:
					case ExpressionType.LessThan:
					case ExpressionType.LessThanOrEqual:
					case ExpressionType.GreaterThan:
					case ExpressionType.GreaterThanOrEqual:
					
						return true;
					case ExpressionType.Call:
						return IsBoolean(((MethodCallExpression)expr).Type);
					case ExpressionType.MemberAccess:
						return IsBoolean(((MemberExpression)expr).Type);
					default:
						return false;
				}
			}

			public static bool IsLogicalOperator(this ExpressionType expressionType) {
				switch (expressionType) {
					case ExpressionType.And:
					case ExpressionType.AndAlso:
					case ExpressionType.Or:
					case ExpressionType.OrElse:
					case ExpressionType.ExclusiveOr:
					case ExpressionType.Not:
						return true;
					default:
						return false;
				}
			}


			public static Expression UpdatePredicate(this ExpressionVisitor visitor, Expression lambda_body) {
				switch (lambda_body.NodeType) {
					case ExpressionType.MemberAccess:
						return Expression.MakeBinary(ExpressionType.Equal, Expression.Constant(true), lambda_body);
					case ExpressionType.Call:
						return (MethodCallExpression)lambda_body;
					default:												
						return (BinaryExpression)lambda_body;
				}					
			}
			public static LambdaExpression GetLambda(this ExpressionVisitor visitor, Expression e) {
				while (e.NodeType == ExpressionType.Quote) {
					e = ((UnaryExpression)e).Operand;
				}
				if (e.NodeType == ExpressionType.Constant) {
					return ((ConstantExpression)e).Value as LambdaExpression;
				}
				return e as LambdaExpression;
			}
			public static Expression StripQuotes(this ExpressionVisitor visitor, Expression e) {
				while (e.NodeType == ExpressionType.Quote) {
					e = ((UnaryExpression)e).Operand;
				}
				return e;
			}

			/// <summary>
			/// Determines whether the CLR type corresponds to a scalar data type in the query language
			/// </summary>
			/// <param name="type"></param>
			/// <returns></returns>
			public static bool IsScalar(this Type type) {
				type = TypeHelper.GetNonNullableType(type);
				switch (Type.GetTypeCode(type)) {
					case TypeCode.Empty:
					case TypeCode.DBNull:
						return false;
					case TypeCode.Object:
						return
							type == typeof(DateTime) ||
							type == typeof(DateTimeOffset) ||
							type == typeof(Decimal) ||
							type == typeof(Guid) ||
							type == typeof(byte[]);
					default:
						return true;
				}
			}
		}

	
}
